package jymsg;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.BufferedReader;
import java.security.MessageDigest;



/**
 *
 * @author daniels
 */
public class YahooSession {

    private short status = YahooPacket.YAHOO_STATUS_AVAILABLE;
    private int session_id = 0;

    private Socket socket;
    private InputStream inSocket;
    private OutputStream outSocket;
    private String username;
    private YahooSessionListener sessionListener;
    

    public void setStatus(short status) {

        this.status = status;
    }

    public short getStatus() {
        
        return this.status;
    }

    public void login(String username, String password)
            throws MalformedURLException, IOException, BadYahooPacketException,
            UsernameOrPasswordMissingException, UsernameHasAtCharException,
            UsernameOrPasswordIncorrectException, SecurityLockForTooManyFaildLoginException,
            SecurityLockException, AccountDeactivatedException, UsernameDoesNotExistsException,
            CrumbSomethingWrongException {

       this.socket = new Socket(this.getPagerHost(), 80);
       this.inSocket = this.socket.getInputStream();
       this.outSocket = this.socket.getOutputStream();
       this.username = username;
       
       YahooPacket pOut = new YahooPacket();
       pOut.setService(YahooPacket.YAHOO_SERVICE_HANDSHAKE);
       pOut.setStatus(this.status);
       pOut.setSessionId(this.session_id);
       pOut.writePacket(this.outSocket);

       YahooPacket pIn = new YahooPacket();
       pIn.readPacket(this.inSocket);
       
       pOut = null;
       pOut = new YahooPacket();
       pOut.setService(YahooPacket.YAHOO_SERVICE_AUTH);
       pOut.setStatus(this.status);
       pOut.setSessionId(this.session_id);
       pOut.insertBody("1", username);
       pOut.writePacket(this.outSocket);

       pIn = null;
       pIn = new YahooPacket();
       pIn.readPacket(this.inSocket);
       this.session_id = pIn.getSessionId();
       String challange = pIn.getBody("94");


       String token = this.getToken(username, password, challange);
       HashMap<String, String> crumbAndCookieYT = this.getCrumbCookieYT(token);
       String encodedHash = this.y64Encode(this.getHash(crumbAndCookieYT.get("crumb"), challange));


       pOut = null;
       pOut = new YahooPacket();
       pOut.setService(YahooPacket.YAHOO_SERVICE_AUTHRESP);
       pOut.setStatus(this.status);
       pOut.setSessionId(this.session_id);
       pOut.insertBody("0", username);
       pOut.insertBody("1", username);
       pOut.insertBody("2", username);
       pOut.insertBody("98", "us");
       pOut.insertBody("135", "9.0.0.2162");
       pOut.insertBody("244", "2097087");
       pOut.insertBody("277", crumbAndCookieYT.get("Y"));
       pOut.insertBody("278", crumbAndCookieYT.get("T"));
       pOut.insertBody("307", encodedHash);
       pOut.writePacket(this.outSocket);

       
       pIn = null;
       pIn = new YahooPacket();
       pIn.readPacket(this.inSocket);
       this.session_id = pIn.getSessionId();

       this.sessionListener = new YahooSessionListener(this.username, this.socket, this.inSocket, this.outSocket, this);
       new Thread(this.sessionListener).start();
    }

    public void logout() throws IOException {

        this.socket.close();
    }

    public boolean isConnected() {

        return !this.socket.isInputShutdown() && !this.socket.isOutputShutdown();
    }

    public void sendMessage(String to, String message) throws IOException {

       YahooPacket pOut = new YahooPacket();
       pOut.setService(YahooPacket.YAHOO_SERVICE_MESSAGE);
       pOut.setStatus(this.status);
       pOut.setSessionId(this.sessionListener.getSessionId());
       pOut.insertBody("0", username);
       pOut.insertBody("1", username);
       pOut.insertBody("5", to);
       pOut.insertBody("14", message);
       pOut.writePacket(this.outSocket);
    }

    public void sendBuzz(String to) throws IOException {
        this.sendMessage(to, "<ding>");
    }

    public void denyBuddy(String who, String reason) throws IOException {

       YahooPacket pOut = new YahooPacket();
       pOut.setService(YahooPacket.YAHOO_SERVICE_DENYBUDDY);
       pOut.setStatus(this.status);
       pOut.setSessionId(this.sessionListener.getSessionId());
       pOut.insertBody("1", username);
       pOut.insertBody("7", who);
       pOut.insertBody("14", reason);
       pOut.writePacket(this.outSocket);
    }

    private String getPagerHost() throws MalformedURLException, IOException {

        URL url = new URL("http://vcs1.msg.yahoo.com/capacity");
        HttpURLConnection http = (HttpURLConnection)url.openConnection();

        http.addRequestProperty("User-Agent", "Mozilla/5.0");
        http.connect();

        InputStreamReader in = new InputStreamReader((InputStream)http.getContent());
        BufferedReader buffer = new BufferedReader(in);
        
        String line;
        do {
            line = buffer.readLine();
            if (line.startsWith("CS_IP_ADDRESS")) {
                http.disconnect();
                return line.split("=")[1];
            }
        } while (line != null);

        http.disconnect();
        return null;
    }

    private String getToken(String username, String password, String challange) 
            throws MalformedURLException, IOException, UsernameOrPasswordMissingException,
            UsernameHasAtCharException, UsernameOrPasswordIncorrectException,
            SecurityLockForTooManyFaildLoginException, SecurityLockException,
            AccountDeactivatedException, UsernameDoesNotExistsException {

        URL url = new URL("https://login.yahoo.com/config/pwtoken_get?src=ymsgr&ts="
                    + "&login=" + URLEncoder.encode(username, "UTF-8")
                    + "&passwd=" + URLEncoder.encode(password, "UTF-8")
                    + "&chal=" + URLEncoder.encode(challange, "UTF-8"));
        HttpURLConnection http = (HttpURLConnection)url.openConnection();

        http.addRequestProperty("User-Agent", "Mozilla/5.0");
        http.connect();

        InputStreamReader in = new InputStreamReader((InputStream)http.getContent());
        BufferedReader buffer = new BufferedReader(in);

        String line = buffer.readLine();
        if (line.equals("0")) {
            http.disconnect();
            String token = buffer.readLine();
            return token.split("=")[1];
        } else if (line.equals("100")) {
            throw new UsernameOrPasswordMissingException();
        } else if (line.equals("1013")) {
            throw new UsernameHasAtCharException();
        } else if (line.equals("1212")) {
            throw new UsernameOrPasswordIncorrectException();
        } else if (line.equals("1213")) {
            throw new SecurityLockForTooManyFaildLoginException();
        } else if (line.equals("1214")) {
            throw new SecurityLockException();
        } else if (line.equals("1218")) {
            throw new AccountDeactivatedException();
        } else if (line.equals("1235")) {
            throw new UsernameDoesNotExistsException();
        } else if (line.equals("1236")) {
            throw new SecurityLockForTooManyFaildLoginException();
        }

        http.disconnect();
        return null;
    }

    private HashMap<String, String> getCrumbCookieYT(String token) throws MalformedURLException, 
            IOException, CrumbSomethingWrongException {

        URL url = new URL("https://login.yahoo.com/config/pwtoken_login?src=ymsgr"
                + "&ts=&token=" + URLEncoder.encode(token, "UTF-8"));
        HttpURLConnection http = (HttpURLConnection)url.openConnection();

        http.addRequestProperty("User-Agent", "Mozilla/5.0");
        http.connect();

        InputStreamReader in = new InputStreamReader((InputStream)http.getContent());
        BufferedReader buffer = new BufferedReader(in);


        HashMap<String, String> result = new HashMap<String, String>();
        String line = buffer.readLine();
        if (!line.equals("0")) {
            throw new CrumbSomethingWrongException();
        }
        String crumbLine = buffer.readLine();
        result.put("crumb", crumbLine.split("=")[1]);

        String yLine = buffer.readLine();
        result.put("Y", yLine.substring(2));

        String tLine = buffer.readLine();
        result.put("T", tLine.substring(2));

        http.disconnect();
        return result;
    }

    private byte[] getHash(String crumb, String challange) {
        MessageDigest md;
        try {
            md = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException ex) {
            return null;
        }

        String data = crumb + challange;

        return md.digest(data.getBytes());
    }

    private String y64Encode(byte[] data) {

        return Base64.encodeToString(data, false).replace("+", ".").replace("/", "_").replace("=", "-");
    }

    public void OnMessage(String from, String to, String message) {

        System.out.println("From <" + from + ">, To <" + to + ">, Message: " + message);
    }
}
